/**
 * @fileoverview simple factory implementation to produce objects as defined in configuration
 * supports deferred initialization of created objects
 *
 * @author Sebastian Brzuzek sebastian@brzuzek.pl
 */

goog.provide('uxperf.FactoryBase');
goog.provide('uxperf.FactoryBase.Autoinit');

goog.require('goog.events');
goog.require('goog.events.Event');
goog.require('uxperf.utils');

/**
 * Return instance of class with optional initialization
 *
 * @param {Object.<string, Object>} map names of classes
 * @param {string} type name of class to instantiate
 * @param {string=} opt_initmode name of automatic initialization mode
 * @param {string|number|null=} opt_delay optional delay (milliseconds) if deffered initialization mode
 * @param {boolean=} opt_caseSensitive flag if class name should be compared in case sensitive mode
 * @param {Function=} opt_callback function to call on success of initialization
 * @param {...*} var_args additional arguments to instance counstructor function (as array or as many arguments)
 * 
 * @return {Object} created instance
 * @throws {Error}
 */
uxperf.FactoryBase.getInstance = function(map, type, opt_initmode, opt_delay, opt_caseSensitive, opt_callback, var_args) {
	if (!map) throw Error('Map of class names not available');
	if (!type) throw Error('Type not set in configuration');
	if (!opt_caseSensitive) type = type.toLowerCase();

if (goog.DEBUG) LOG.info('getInstance of ' + type);
	
  var class_ = map[type];
	if (!class_) throw Error('Unknown class');
	
	var args = uxperf.utils.optionalArgs(6, arguments, true);
  var inst = Object.create(class_.prototype);
	inst = class_.apply(inst, args) || inst; //constructor
  
	var instance = /** @type {uxperf.FactoryBase.Autoinit} */ inst;

	if (opt_callback) {
		if (instance instanceof goog.events.EventTarget) {
			goog.events.listenOnce(instance, uxperf.FactoryBase.EVENT_TYPE_READY, opt_callback, false, instance);
if (goog.DEBUG) LOG.info('added ready event listener to instance of ' + type);
		}

		if (!instance.init) {
			//if there is no init method - initialization done.
if (goog.DEBUG) LOG.info('there is no init method for instance of ' + type);
		
			if (instance instanceof goog.events.EventTarget) {
				instance.dispatchEvent(uxperf.FactoryBase.EVENT_TYPE_READY);
			} else if (typeof opt_callback === 'function') {
				opt_callback.call(instance, new goog.events.Event(uxperf.FactoryBase.EVENT_TYPE_READY, instance));
			}
		
      return instance;
    }
  }
	
	opt_initmode = opt_initmode || uxperf.FactoryBase.AUTOINIT_MODE.ON_CONSTRUCTION;

if (goog.DEBUG) LOG.info('init mode for instance of ' + type + ' is ' + opt_initmode);
	
	switch (opt_initmode) {
		case uxperf.FactoryBase.AUTOINIT_MODE.ON_CONSTRUCTION:
if (goog.DEBUG) LOG.info('before init');
			instance.init.call(instance);
if (goog.DEBUG) LOG.info('init called for instance of ' + type);
			break;
		case uxperf.FactoryBase.AUTOINIT_MODE.ON_DOM_READY:
			//at this moment there is no standard support for dom ready event in Closure Library
			//so we use load event
			goog.events.listenOnce(document, goog.events.EventType.LOAD, function () {
				instance.init.call(instance);
			});
			break;
		case uxperf.FactoryBase.AUTOINIT_MODE.DEFERRED:
			opt_delay = opt_delay || 1;
			if (!goog.isNumber(opt_delay)) opt_delay = Number(opt_delay);
			setTimeout(function () { instance.init.call(instance); }, opt_delay);
if (goog.DEBUG) LOG.info('init scheduled for instance of ' + type + ' in ' + opt_delay + ' ms');
			break;
		case uxperf.FactoryBase.AUTOINIT_MODE.MANUAL:
if (goog.DEBUG) LOG.info('required manual initialization instance of ' + type);
			break;
		default:
			throw Error('Unknown target initialization mode');
	}
	
	return instance;
};

/**
 * constants for auto init modes
 * @enum {string}
 */
uxperf.FactoryBase.AUTOINIT_MODE = {
	ON_CONSTRUCTION: 'on_construction',
	ON_DOM_READY: 'on_dom_ready',
	DEFERRED: 'deferred',
	MANUAL: 'manual'
};

/**
 * Initialization finished event name
 * 
 * @const
 */
uxperf.FactoryBase.EVENT_TYPE_READY = 'ready';

/**
 * Class with support for automatic initialization
 * 
 * @interface
 */
uxperf.FactoryBase.Autoinit = function() {};

/**
 * method used for automatic initialization
 * @return {Object} initialized instance
 */
uxperf.FactoryBase.Autoinit.prototype.init = goog.nullFunction;
